package ch.zhaw.cctd.domain.shot;

import java.awt.Dimension;
import java.awt.Point;
import java.io.Serializable;
import java.util.Calendar;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.zhaw.cctd.domain.GraphicalElement;
import ch.zhaw.cctd.domain.Movable;
import ch.zhaw.cctd.domain.Shootable;
import ch.zhaw.cctd.domain.Shooter;
import ch.zhaw.cctd.domain.map.MapElement;

/**
 * Representiert ein Schuss auf der Map. Ist zuständig für den Zustand des
 * Schusses, Prüft ob der Schuss getroffen hat und regelt den Effekt des
 * Schusses. Regelt auch den Geldfluss zum Spieler.
 * 
 * @author Rolf Koch
 */
public class Shot extends MapElement implements GraphicalElement, Movable, Serializable {

	/**
	 * Serializable ID
	 */
	private static final long serialVersionUID = 1559924922454407087L;

	/**
	 * Logger
	 */
	private static final Logger logger = LoggerFactory.getLogger(Shot.class);

	/**
	 * Shot Layer on the JLablePane
	 */
	private static final int SHOT_LAYER_LEVEL = 20;

	//Shot Informations
	private transient int speed;
	private transient Shooter source;
	private transient Shootable target;
	
	
	
	// Zustand
	boolean targetHit = false;
	private transient long startTime;

	/**
	 * Default Constructor
	 * @param position
	 * @param size
	 * @param texture
	 * @param speed
	 * @param source
	 * @param target
	 * @param debuffs
	 * @param map
	 */
	public Shot(Point position, Dimension size, String texture, int speed, Shooter source, Shootable target) {
		super(position, size, texture, SHOT_LAYER_LEVEL);
		this.speed = speed;
		this.source = source;
		this.target = target;

		this.startTime = 0;
		this.targetHit = false;
	}

	/**
	 * Move the Shot
	 */
	public void move() {
		logger.trace("move SHOT");
		if (!isTargetHit()) {
			// logger.trace("Moving Shot");

			// Is already Moving, Calculating Position
			long nowTime = Calendar.getInstance().getTimeInMillis();

			// Get Factor
			double wayLength = (nowTime - startTime) * this.speed / 1000;
			double totalWayLength = getCenter().distance(target.getCenter());
			double factor = wayLength / totalWayLength;
			factor = Math.min(1.0, factor);

			// Calulate X and Y of the current path.
			// Zielpunkt = Startpunkt + faktor*(Endpunkt-Startpunkt)
			double xCord = getCenter().getX() + factor * (target.getCenter().getX() - getCenter().getX());
			double yCord = getCenter().getY() + factor * (target.getCenter().getY() - getCenter().getY());
			Point newCenterPoint = new Point((int) xCord, (int) yCord);
			// logger.trace("new Point: " + newCenterPoint + " Target: " +
			// target.getCenter() + " Distance: " +
			// newCenterPoint.distance(target.getCenter()));
			// New Centerpoint
			setNewCenter(newCenterPoint);
			// Log
			// logger.trace("New Position: [" + (int) getCenter().getX() + ":" +
			// (int) getCenter().getY() + "]  Target: [" + (int)
			// target.getCenter().getX() + ":"
			// + (int) target.getCenter().getY() + "] Speed: " + this.speed);

			// New Starttime
			startTime = nowTime;

			// Check if we hit
			checkTargetHit();
		} else {
			startTime = 0;
		}
	}

	/**
	 * Set the Shot to start moving around
	 */
	public void startMoving() {
		logger.trace("start moving");
		startTime = Calendar.getInstance().getTimeInMillis();
	}

	/** 
	 * Checks if the Shot is moving
	 * @return boolean
	 */
	public boolean isMoving() {
		return (startTime > 0);
	}

	/**
	 * Check if the Shot has Hit his target
	 * If yes: apply Effects to the target and if its killed, collect the Money
	 */
	public void checkTargetHit() {
		
		logger.trace("checkTargetHit");
		if (!isTargetHit()) {
			// Not yet Hit.. Lets check if we have a hit
			double distance = this.getCenter().distance(target.getCenter());
			logger.trace("Distanz: {} < {}", distance, (getSize().getWidth() / 2));
			if (distance <= (getSize().getWidth() / 2)) {
				logger.trace("Hit");
				// Target is reached HIT IT
				this.source.targetHit(this.getCenter());
				
				targetHit = true;
				// Hit Finished
			}
		}
	}

	/**
	 * Check if Shot has hit
	 * @return boolean
	 */
	public boolean isTargetHit() {
		return targetHit;
	}

}
